package com.lpf.nio;

import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.Iterator;
import java.util.Set;

/**
 * 功能描述：服务器端会监听5个端口号，每个端口号都可以由客户端向其发起连接。
 *
 * @author lpf
 * @date 2019/11/26 11:08
 * @version: 1.0
 **/
public class NioTest12 {

    public static void main(String arg[]) throws Exception {

        System.out.println(SelectorProvider.provider().getClass()); // sun.nio.ch.WindowsSelectorProvider
        // java.nio.channels.spi.SelectorProvider.provider 中
        System.out.println(sun.nio.ch.DefaultSelectorProvider.create().getClass()); // sun.nio.ch.WindowsSelectorProvider


        int posts[] = {5000, 5001, 5002, 5003, 5004};

        // 选择器其实是针对的客户端的连接
        Selector selector = Selector.open();

        // 异步编程模型离不开一个概念：事件

        for (int port : posts) {
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false); // 非阻塞
            ServerSocket socket = serverSocketChannel.socket();
            socket.bind(new InetSocketAddress(port)); // 绑定操作

            // 监听不同类型事件 accept、Connect、read、write
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("监听端口：" + port);
        }

        while (true) {
            //selector.select(); 若key对应的channel准备好进行I/O操作,返回key的数量
            //有返回，说明事件发生了
            int numbers = selector.select();
            System.out.println("numbers:" + numbers);

            //返回这个selector的selected-key的集合,因为监听多个通道，可能有两个以上通道返回，所以用集合。
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            System.out.println("selectionKeys:" + selectionKeys);

            Iterator<SelectionKey> itr = selectionKeys.iterator();
            while (itr.hasNext()) {

                SelectionKey selectionKey = itr.next();
                if (selectionKey.isAcceptable()) {
                    ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();

                    // 这个表示真正的连接的通道
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    socketChannel.configureBlocking(false);

                    // 连接已建立好，注册到selector中
                    socketChannel.register(selector, SelectionKey.OP_READ);

                    // 必须移除掉，否则的话还会监听这个事件
                    itr.remove();

                    System.out.println("获得客户端连接：" + socketChannel);
                } else if (selectionKey.isReadable()) {

                    // channel是双向的，可读可写
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();

                    int byteRead = 0;

                    while (true) {
                        ByteBuffer byteBuffer = ByteBuffer.allocate(512);
                        byteBuffer.clear();

                        int read = socketChannel.read(byteBuffer);
                        if (read < 0) break;

                        byteBuffer.flip();

                        socketChannel.write(byteBuffer);

                        byteRead += read;
                    }

                    System.out.println("读取：" + byteRead + "，来自于：" + socketChannel);

                    // 当前的事件已经消费掉了，一定要remove
                    itr.remove();
                }
            }
        }
    }
}
